package dk.degofedal.heating

import java.io.OutputStream
import java.io.InputStream
import gnu.io.SerialPortEvent
import gnu.io.SerialPortEventListener
import scala.actors.ReplyReactor
import gnu.io.SerialPort
import gnu.io.CommPortIdentifier
import compat.Platform._
import scala.actors.Actor._

class AVR(act: ReplyReactor, var path: String) extends SerialPortEventListener {

  //def ?[A <: AnyRef](nullable: A): Option[A] = if (nullable eq null) None else Some(nullable)
  //val foo: Option[Foo] = ?(getFooFromJavaAPIThatMightReturnNull())
  
  val serialPort : Option[SerialPort] = getSerialPort

  private def getSerialPort : Option[SerialPort] = {
    val TIME_OUT = 2000
    val DATA_RATE = 9600
  
    // initialize
    var portId : Option[CommPortIdentifier] = None
    var serialPort : Option[SerialPort] = None
    val portEnum = CommPortIdentifier.getPortIdentifiers()
    // iterate through, looking for the port
    while (portEnum.hasMoreElements()) {
      val currPortId : CommPortIdentifier = portEnum.nextElement().asInstanceOf[CommPortIdentifier]
      if (currPortId.getName().equals(path)) {
        portId = Some(currPortId);
      }
    }
    
    if (!portId.isEmpty) {
      
      // open serial port, and use class name for the appName.
      serialPort = Some(portId.get.open(this.getClass().getName(),TIME_OUT).asInstanceOf[SerialPort])
      
      try {
        // set port parameters
        serialPort.get.setSerialPortParams(DATA_RATE,
				       SerialPort.DATABITS_8,
				       SerialPort.STOPBITS_1,
				       SerialPort.PARITY_NONE)
        
      } catch {
        case e: Exception => println(e.toString())
      }
      println("SerialPort set up")
    } else {
      println("Could not find COM port. - exiting")
    }

    serialPort
  }
  
  def serialEvent(event: SerialPortEvent) {
    val eventType : Int = event.getEventType()
    typeSwitch(eventType)
    
    def typeSwitch(eventType: Int) = eventType match {
      case SerialPortEvent.DATA_AVAILABLE => readInput(serialPort)
      case SerialPortEvent.BI => println("BI")
      case SerialPortEvent.CD => println("CD")
      case SerialPortEvent.CTS => println("CTS")
      case SerialPortEvent.DSR => println("DSR")
      case SerialPortEvent.FE => println("FE")
      case SerialPortEvent.OE => println("OE")
      case SerialPortEvent.OUTPUT_BUFFER_EMPTY => println("Output Buffer empty")
      case SerialPortEvent.PE => println("PE")
      case SerialPortEvent.RI => println("RI")
      case _ => println("Unknown eventType ",eventType)
    }
  }
  
  private def readInput(serialPort : Option[SerialPort]) = {
    if(!serialPort.isEmpty){
      try {
        val input : InputStream = serialPort.get.getInputStream()
        
        var available = input.available()
        var temp = 0
        while (available != temp) { 
	  // something that takes a little time
	  Thread.sleep(100)
	  temp = available
	  available = input.available
        }
        
        val chunk = new Array[Byte](available)
        input.read(chunk, 0, available)
        input.close()
        
        /*for(byte:Byte <- chunk) {
         byte match {
         case QUIT_SIGNAL => recievedQuit = true;
         case START_TRANSMISSION  => handshake;
         case _ => ;
         }
         }*/
        // Displayed results are codepage dependent
        
        // handle chunk (store data)
        
        // send warning if needed, etc.
        act ! new String(chunk)
      } catch {
        case e : Exception => System.err.println(e.toString())
      }
    } else {
      println("AVR not attached")
    }
  }
  
  def send(data : String) = {
    if(!serialPort.isEmpty) {
      val output : OutputStream = serialPort.get.getOutputStream()
      output.write(data.getBytes)
      output.close
    } else {
      println("AVR not attached - not sending")
    }
  }
  
  def start() = {
    if(!serialPort.isEmpty) {
      // add event listeners
      serialPort.get.addEventListener(this)
      serialPort.get.notifyOnDataAvailable(true)
      println("AVR Attached")
    } else {
      println("AVR not attached - not starting")
    }
  } 

  def stop() = {
    if(!serialPort.isEmpty) {
      serialPort.get.removeEventListener()
      serialPort.get.close()
      println("AVR Detached")
    } else {
      println("AVR not attached - not stopping")
    }
  }
}
